/// @file UIniFile.h Ini File Reader/Writer Class
#pragma once
#include "../stdafx.h"
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <functional>

// boost stuff
#include <boost/shared_ptr.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>

// helper structs and typedefs
namespace ini_helper {
	namespace ini_boost {
		using namespace boost;
		using namespace boost::multi_index;

		struct INIKeyValuePair {
			std::string key;
			std::string value;
			INIKeyValuePair(const std::string & akey, const std::string & avalue)
				: key(akey), value(avalue) {};
		};
		typedef shared_ptr<INIKeyValuePair> INIKeyValuePairRef;

		typedef multi_index_container<
			INIKeyValuePairRef,
			indexed_by<
				ordered_non_unique<member<INIKeyValuePair, std::string, &INIKeyValuePair::key> >,
				sequenced<> // list-like index
			>
		> KeyValueMap;

		struct INISection {
			std::string name;
			KeyValueMap keyvals;
			INISection(const std::string & aname) : name(aname) {};
		};
		typedef shared_ptr<INISection> INISectionRef;

		typedef multi_index_container<
			INISectionRef,
			indexed_by<
				ordered_unique<member<INISection, std::string, &INISection::name> >,
				sequenced<> // list-like index
			>
		> SectionMap;	
	}

	typedef ini_boost::KeyValueMap KeyValueMap;
	typedef ini_boost::SectionMap SectionMap;
	typedef ini_boost::INIKeyValuePairRef INIKeyValuePairRef;
	typedef ini_boost::INISectionRef INISectionRef;
	typedef ini_boost::INIKeyValuePair INIKeyValuePair;
	typedef ini_boost::INISection INISection;
}

using namespace ini_helper;

/** class for parsing/loading/saving ini files 
 *  Represents the contents of an ini file
 */
class TIniFile {
	public:
		/** Construct empty TIniFile */
		TIniFile();

		/** Construct TIniFile from an ini file
		 *
		 *  @param FileName The filename of the ini file to read from
		 */
		TIniFile(const std::string & FileName);

		/** Construct TIniFile from a stream
		 *
		 *  @param stream The stream to read from
		 */
		TIniFile(std::ifstream & stream);

		/** Destructor */
		~TIniFile();

		/** Load contents from an ini file
		 *
		 *  Previous contents are lost
		 *  @param FileName The filename of the ini file to read from
		 *  @return Whether the loading succeeded
		 */
		bool Load( const std::string & FileName );

		/** Load contents from a stream
		 *
		 *  Previous contents are lost
		 *  @param stream The stream to read from
		 *  @return Whether the loading succeeded
		 */
		bool Load( std::ifstream & inFile );

		/** Save contents to an ini file
		 *
		 *  @param FileName The filename of the ini file to write to
		 *  @return Whether the saving succeeded
		 */
		bool Save(const std::string & FileName ) const;

		/** Save contents to last ini file loaded from
		 *
		 *  Only works when constructed/last Loaded by Filename (not by stream)
		 *  @return Whether the saving succeeded
		 */
		bool Save() const;

		/** Add a section to the contents
		 *
		 *  @param sectionname Name of the section to add
		 */
		void addSection(const std::string & sectionname);

		/** Remove a section from the contents
		 *
		 *  @param sectionname Name of the section to remove
		 */
		void removeSection(const std::string & sectionname);

		/** Check if a section exists in the contents
		 *
		 *  @param sectionname Name of the section to check
		 *  @return Whethe the section exists
		 */
		bool hasSection(const std::string & sectionname) const;

		/** Set the value of a key
		 *
		 *  @param sectionname Name of the section the key is in
		 *  @param keyname Name of the key which value to set
		 *  @param valuename New value of the specified key
		 */
		void setValue(const std::string & sectionname, const std::string & keyname, const std::string &  valuename);

		/** Get the value of a key
		 *
		 *  @param sectionname Name of the section the key is in
		 *  @param keyname Name of the key which value to get
		 *  @return Value of the specified key
		 */
		std::string getValue(const std::string & sectionname, const std::string & keyname) const;

		/** Check if a key exists (has a value)
		 *
		 *  @param sectionname Name of the section the key is in
		 *  @param keyname Name of the key which value to get
		 *  @return Whether the specifed key exists
		 */
		bool hasValue(const std::string & sectionname, const std::string & keyname) const;

	private:
		SectionMap inidata;

		std::string file;

		void clear();
};
